package de.mmis.core.publishsubscribe.filter;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;

import de.mmis.core.base.abstracttree.Tree;
import de.mmis.core.base.abstracttree.TreeParserException;
import de.mmis.core.sexpression.SExpressionParser;
import de.mmis.core.sexpression.UndoInputStream;

class Lexer implements de.mmis.core.publishsubscribe.filter.Parser.Lexer {

	private static class TokenListEntry {
		String token;
		Object lval;
		int result;

		public TokenListEntry(String token, Object lval, int result) {
			this.token = token;
			this.lval = lval;
			this.result = result;
		}
	}

	private final static TokenListEntry[] tokenList;

	static {
		// take care of the order - longer tokens first
		tokenList = new TokenListEntry[] {
				new TokenListEntry("==", FilterOperator.IDENTICAL, Parser.fop),
				new TokenListEntry("=>", BooleanOperator.IMPLIES,
						Parser.bop_implies),
				new TokenListEntry("=", FilterOperator.EQUAL, Parser.fop),

				new TokenListEntry("!==", FilterOperator.NOT_IDENTICAL,
						Parser.fop),
				new TokenListEntry("!=", FilterOperator.NOT_EQUAL, Parser.fop),
				new TokenListEntry("!~", FilterOperator.NOT_MATCH, Parser.fop),
				new TokenListEntry("!", null, Parser.not),
				new TokenListEntry("~", FilterOperator.MATCH, Parser.fop),

				new TokenListEntry("<=>", BooleanOperator.IFF, Parser.bop_iff),
				new TokenListEntry("<=", FilterOperator.LESS_EQUAL, Parser.fop),
				new TokenListEntry("<", FilterOperator.LESS, Parser.fop),
				new TokenListEntry(">=", FilterOperator.GREATER_EQUAL,
						Parser.fop),
				new TokenListEntry(">", FilterOperator.GREATER, Parser.fop),

				new TokenListEntry("&&", BooleanOperator.AND, Parser.bop_and),
				new TokenListEntry("||", BooleanOperator.OR, Parser.bop_or),
				new TokenListEntry("^^", BooleanOperator.XOR, Parser.bop_xor),

				new TokenListEntry("(", null, Parser.lpar),
				new TokenListEntry(")", null, Parser.rpar) };
	}

	private String s;

	private String lastToken;
	private Object lval = null;

	public Lexer(String s) {
		this.s = s;
	}

	@Override
	public Object getLVal() {
		return lval;
	}

	@Override
	public int yylex() throws IOException {
		s = s.trim();

		if (s.isEmpty()) {
			lastToken = "EOF";
			return Parser.EOF;
		}

		for (TokenListEntry entry : tokenList) {
			String token = entry.token;
			if (s.startsWith(token)) {
				s = s.substring(token.length());
				lval = entry.lval;
				lastToken = token;
				return entry.result;
			}
		}

		char c1 = s.charAt(0);

		if (s.length() > 1) {
			char c2 = s.charAt(1);

			// references start with $
			boolean isReference = c1 == '$';

			// it may start with $:
			// but the next character must be a Java Identifier
			if (c2 == ':') {
				if (s.length() <= 2)
					isReference = false;
				else if (!Character.isJavaIdentifierPart(s.charAt(2)))
					isReference = false;
			} else if (!Character.isJavaIdentifierPart(c2))
				isReference = false;

			boolean isID = c1 == '@' && Character.isJavaIdentifierPart(c2);

			if (isReference || isID) {
				StringBuffer ref = new StringBuffer();
				int i = 1;
				char c = s.charAt(i);

				do {
					ref.append(c);
					i++;
					if (i >= s.length())
						break;
					c = s.charAt(i);
				} while (Character.isJavaIdentifierPart(c));

				lval = ref.toString();
				s = s.substring(i);
				lastToken = (String) lval;
				if (isReference)
					return Parser.reference;
				else if (isID)
					return Parser.id;

			}
		}
		// it must be an S-Expression

		// System.out.println("\nExpecting S-Expression in input:\n" + s);

		Tree exp;
		InputStream is = new UndoInputStream(new ByteArrayInputStream(
				s.getBytes()));
		boolean gotBracket = false;
		if (c1 == '[') {
			// skip the [
			is.read();
			gotBracket = true;
		}

		try {
			exp = SExpressionParser.parse(is, false);
		} catch (TreeParserException e) {
			throw new IOException(e);
		}

		lval = exp;

		s = "";
		InputStreamReader reader = new InputStreamReader(is);
		char buf[] = new char[1024];
		int read;

		while ((read = reader.read(buf)) > 0) {
			s = s + String.copyValueOf(buf, 0, read);
		}

		// System.out
		// .println("Matched S-Expression:\n" + exp + "\nRest is:\n" + s);

		lastToken = exp.toString();

		if (gotBracket) {
			s = s.trim();
			if (s.charAt(0) != ']')
				throw new IOException("Expecting ']', got " + s.charAt(0));
			s = s.substring(1);
		}

		return Parser.sexpression;
	}

	@Override
	public void yyerror(String s) {
		System.err.println(s + " after token: " + lastToken);
	}
}
